using System;
using Driven.ProcessScheduler.Interfaces;
using NUnit.Framework;
using Rhino.Mocks;

namespace Driven.ProcessScheduler.Test
{
    [TestFixture]
    public class ScheduleTest
    {
        [Test]
        public void CanCreateTimeScheduledComponent()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Parse("11:30");
            const int REPETITION_SCHEDULE = 0;
            const string name = "S1";

            var schedule = new Schedule(name, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.AreEqual(schedule.Name, name);
            Assert.AreSame(schedule.Component, component);
            Assert.AreEqual(schedule.TimeSchedule, timeSchedule);
            Assert.AreEqual(schedule.RepetitionSchedule, REPETITION_SCHEDULE);
        }

        [Test]
        public void CanCreateRepetitionScheduledComponent()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Zero;
            const int REPETITION_SCHEDULE = 90;
            const string name = "S1";

            var schedule = new Schedule(name, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.AreEqual(schedule.Name, name);
            Assert.AreSame(schedule.Component, component);
            Assert.AreEqual(schedule.TimeSchedule, timeSchedule);
            Assert.AreEqual(schedule.RepetitionSchedule, REPETITION_SCHEDULE);
        }

        [Test]
        public void NextRunDateTimeIsCorrectForTimeScheduledComponent()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Parse("11:30");
            const int REPETITION_SCHEDULE = 0;
            var tomorrow = DateTime.Today.Add(timeSchedule).AddDays(1);

            var schedule = new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.That(schedule.NextRunDateTime, Is.LessThan(DateTime.Now));

            schedule.Execute();

            Assert.That(schedule.NextRunDateTime, Is.EqualTo(tomorrow));
        }


        [Test]
        public void NextRunDateTimeIsCorrectForRepetitionScheduledComponent()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Zero;
            const int REPETITION_SCHEDULE = 90;

            var schedule = new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);
            
            Assert.That(schedule.NextRunDateTime, Is.LessThan(DateTime.Now));

            schedule.Execute();

            var nextRun = DateTime.Now.AddMinutes(REPETITION_SCHEDULE);

            Assert.That(schedule.NextRunDateTime, Is.EqualTo(nextRun));
        }

        [Test]
        public void CannotCreateScheduleWithBothTimeAndRepetitionSchedules()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Parse("11:30");
            const int REPETITION_SCHEDULE = 90;

            try
            {
                new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);
                Assert.Fail("Should not be possible to create a Schedule with both a time and a repetition schedule");
            }
            catch (InvalidOperationException) { /* expected */ }

        }

        [Test]
        public void ScheduleIsNoLongerDueAfterExecuting()
        {

            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Parse("11:30");
            const int REPETITION_SCHEDULE = 0;

            var schedule = new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.That(schedule.IsDue);

            schedule.Execute();

            Assert.That(schedule.IsDue, Is.False);

        }


        [Test]
        public void LastRanDateIsSet()
        {
            var beforeExecuting = DateTime.Now.AddSeconds(-1); // Subtract a second because otherwise schedule executes too fast to get different LastRan value
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Parse("11:30");
            const int REPETITION_SCHEDULE = 0;

            var schedule = new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.That(schedule.LastRan == DateTime.MaxValue);

            schedule.Execute();

            Assert.That(schedule.LastRan != DateTime.MaxValue);
            Assert.That(schedule.LastRan > beforeExecuting);
        }

        [Test]
        public void ChangingScheduleFromRepetitionToTimeClearsTime()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Parse("11:30");
            const int REPETITION_SCHEDULE = 0;

            var schedule = new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.AreEqual(schedule.TimeSchedule, timeSchedule);

            schedule.RepetitionSchedule = 90;

            Assert.AreEqual(schedule.TimeSchedule, TimeSpan.Zero);

        }

        [Test]
        public void ChangingScheduleFromTimeToRepetitionClearsRepetition()
        {
            var component = MockRepository.GenerateStub<IScheduledComponent>();
            var timeSchedule = TimeSpan.Zero;
            const int REPETITION_SCHEDULE = 90;

            var schedule = new Schedule(string.Empty, component, timeSchedule, REPETITION_SCHEDULE);

            Assert.AreEqual(schedule.RepetitionSchedule, REPETITION_SCHEDULE);

            schedule.TimeSchedule = TimeSpan.Parse("11:30");

            Assert.AreEqual(schedule.RepetitionSchedule, 0);
        }
    }
}
